using System;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace DynamicProxy
{
	public class ProxyFactory
	{
		private const string PROXY_SUFFIX = "Proxy";

		private const string ASSEMBLY_NAME = "ProxyAssembly";

		private const string MODULE_NAME = "ProxyModule";

		private const string HANDLER_NAME = "handler";

		private static ProxyFactory instance;

		private static object lockObj;

		private Hashtable typeMap = Hashtable.Synchronized(new Hashtable());

		private static readonly Hashtable opCodeTypeMapper;

		private static readonly MethodInfo INVOKE_METHOD;

		private static readonly MethodInfo GET_METHODINFO_METHOD;

		static ProxyFactory()
		{
			lockObj = new object();
			opCodeTypeMapper = new Hashtable();
			INVOKE_METHOD = typeof(IProxyInvocationHandler).GetMethod("Invoke");
			GET_METHODINFO_METHOD = typeof(MetaDataFactory).GetMethod("GetMethod", new Type[2]
			{
				typeof(string),
				typeof(int)
			});
			opCodeTypeMapper.Add(typeof(bool), OpCodes.Ldind_I1);
			opCodeTypeMapper.Add(typeof(short), OpCodes.Ldind_I2);
			opCodeTypeMapper.Add(typeof(int), OpCodes.Ldind_I4);
			opCodeTypeMapper.Add(typeof(long), OpCodes.Ldind_I8);
			opCodeTypeMapper.Add(typeof(double), OpCodes.Ldind_R8);
			opCodeTypeMapper.Add(typeof(float), OpCodes.Ldind_R4);
			opCodeTypeMapper.Add(typeof(ushort), OpCodes.Ldind_U2);
			opCodeTypeMapper.Add(typeof(uint), OpCodes.Ldind_U4);
		}

		private ProxyFactory()
		{
		}

		public static ProxyFactory GetInstance()
		{
			if (instance == null)
			{
				CreateInstance();
			}
			return instance;
		}

		private static void CreateInstance()
		{
			lock (lockObj)
			{
				if (instance == null)
				{
					instance = new ProxyFactory();
				}
			}
		}

		public object Create(IProxyInvocationHandler handler, Type objType, bool isObjInterface)
		{
			string text = objType.FullName + "Proxy";
			Type type = (Type)typeMap[text];
			if (type == null)
			{
				type = ((!isObjInterface) ? CreateType(handler, objType.GetInterfaces(), text) : CreateType(handler, new Type[1]
				{
					objType
				}, text));
				typeMap.Add(text, type);
			}
			return Activator.CreateInstance(type, handler);
		}

		public object Create(IProxyInvocationHandler handler, Type objType)
		{
			return Create(handler, objType, isObjInterface: false);
		}

		private Type CreateType(IProxyInvocationHandler handler, Type[] interfaces, string dynamicTypeName)
		{
			Type result = null;
			if (handler != null && interfaces != null)
			{
				Type typeFromHandle = typeof(object);
				Type typeFromHandle2 = typeof(IProxyInvocationHandler);
				AppDomain domain = Thread.GetDomain();
				AssemblyName assemblyName = new AssemblyName();
				assemblyName.Name = "ProxyAssembly";
				assemblyName.Version = new Version(1, 0, 0, 0);
				AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
				ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("ProxyModule");
				TypeAttributes attr = TypeAttributes.Public | TypeAttributes.Sealed;
				TypeBuilder typeBuilder = moduleBuilder.DefineType(dynamicTypeName, attr, typeFromHandle, interfaces);
				FieldBuilder fieldBuilder = typeBuilder.DefineField("handler", typeFromHandle2, FieldAttributes.Private);
				ConstructorInfo constructor = typeFromHandle.GetConstructor(new Type[0]);
				ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[1]
				{
					typeFromHandle2
				});
				ILGenerator iLGenerator = constructorBuilder.GetILGenerator();
				iLGenerator.Emit(OpCodes.Ldarg_0);
				iLGenerator.Emit(OpCodes.Ldarg_1);
				iLGenerator.Emit(OpCodes.Stfld, fieldBuilder);
				iLGenerator.Emit(OpCodes.Ldarg_0);
				iLGenerator.Emit(OpCodes.Call, constructor);
				iLGenerator.Emit(OpCodes.Ret);
				foreach (Type interfaceType in interfaces)
				{
					GenerateMethod(interfaceType, fieldBuilder, typeBuilder);
				}
				result = typeBuilder.CreateType();
			}
			return result;
		}

		private void GenerateMethod(Type interfaceType, FieldBuilder handlerField, TypeBuilder typeBuilder)
		{
			MetaDataFactory.Add(interfaceType);
			MethodInfo[] methods = interfaceType.GetMethods();
			PropertyInfo[] properties = interfaceType.GetProperties();
			for (int i = 0; i < methods.Length; i++)
			{
				MethodInfo methodInfo = methods[i];
				ParameterInfo[] parameters = methodInfo.GetParameters();
				int num = parameters.Length;
				Type[] array = new Type[num];
				for (int j = 0; j < num; j++)
				{
					array[j] = parameters[j].ParameterType;
				}
				MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodInfo.Name, methodInfo.Attributes & ~MethodAttributes.Abstract, CallingConventions.Standard, methodInfo.ReturnType, array);
				ILGenerator iLGenerator = methodBuilder.GetILGenerator();
				iLGenerator.Emit(OpCodes.Ldarg_0);
				iLGenerator.Emit(OpCodes.Ldfld, handlerField);
				iLGenerator.Emit(OpCodes.Ldarg_0);
				iLGenerator.Emit(OpCodes.Ldstr, interfaceType.FullName);
				iLGenerator.Emit(OpCodes.Ldc_I4, i);
				iLGenerator.Emit(OpCodes.Call, GET_METHODINFO_METHOD);
				iLGenerator.Emit(OpCodes.Ldc_I4, num);
				iLGenerator.Emit(OpCodes.Newarr, typeof(object));
				for (int j = 0; j < num; j++)
				{
					iLGenerator.Emit(OpCodes.Dup);
					iLGenerator.Emit(OpCodes.Ldc_I4, j);
					iLGenerator.Emit(OpCodes.Ldarg, j + 1);
					if (array[j].IsValueType)
					{
						iLGenerator.Emit(OpCodes.Box, array[j]);
					}
					iLGenerator.Emit(OpCodes.Stelem_Ref);
				}
				iLGenerator.Emit(OpCodes.Callvirt, INVOKE_METHOD);
				if (methodInfo.ReturnType != typeof(void))
				{
					if (methodInfo.ReturnType.IsValueType)
					{
						iLGenerator.Emit(OpCodes.Unbox, methodInfo.ReturnType);
						if (methodInfo.ReturnType.IsEnum)
						{
							iLGenerator.Emit(OpCodes.Ldind_I4);
						}
						else if (!methodInfo.ReturnType.IsPrimitive)
						{
							iLGenerator.Emit(OpCodes.Ldobj, methodInfo.ReturnType);
						}
						else
						{
							iLGenerator.Emit((OpCode)opCodeTypeMapper[methodInfo.ReturnType]);
						}
					}
				}
				else
				{
					iLGenerator.Emit(OpCodes.Pop);
				}
				iLGenerator.Emit(OpCodes.Ret);
			}
			Type[] interfaces = interfaceType.GetInterfaces();
			foreach (Type interfaceType2 in interfaces)
			{
				GenerateMethod(interfaceType2, handlerField, typeBuilder);
			}
		}
	}
}
